#ifndef __ThreadPool__HPP__
#define __ThreadPool__HPP__

#include <iostream>
#include <queue>
#include "thread.hpp"
#include "log.hpp"
#include "mutex.hpp"
#include "cond.hpp"

namespace ThreadPoolModule
{
    using namespace MyThread;
    using namespace MutexModule;
    using namespace MyCond;
    using namespace MyLog;

    // 默认使用5个线程的线程池
    const int defaultThreadSize = 5;

    template <typename T>
    class ThreadPool
    {
    private:
        void WakeUpAll()
        {
            if(_sleepernumber){
                LOG(loglevel::INFO) << "唤醒所有线程";
                _cond.broadcast();
            }
        }

        void WakeUpOne()
        {
            LOG(loglevel::INFO) << "唤醒一个线程";
            _cond.signal();
        }
        
        // 同一时刻，内存中不需要存在多个线程池
        // 利用单例模式来控制该进程池只能实例化出一个对象：单例模型即一个类只能实例化一个对象
        // 单例模式有两种实现方式：饿汉模型和懒汉模式
        // 饿汉模式：在将代码加载到内存中时，就已经初始化了该对象
        // 懒汉模式：在代码加载到内存中时，只初始化一个该类对象的指针，并不具体实例化。当真正使用的时候，在进行实例化
        // 在一个类比较大的时候，在加载的时候直接创建对象比较耗时
        // 懒汉模式采用延时创建技术，就可以加快启动进程的时候
        // 在内核中，我们使用malloc申请内存空间，其实就使用了懒汉模式，先给你虚拟地址空间，当你使用该虚拟地址空间的时候，再给你从内存中开辟，并构建映射关系

        // 因为单例模式只能创建一个对象，所以不应该将类的构造，拷贝构造，赋值重载函数公开

        ThreadPool(const int threads = defaultThreadSize)
            :_num(threads), _isRunning(true), _sleepernumber(0)
        {
            // 创建_num个线程
            for(int i=0; i<_num; i++){
                _threads.emplace_back(
                    [this](){
                        Handler();
                    }
                );
            }
        }

        ThreadPool(const ThreadPool& tp) = delete;
        ThreadPool operator=(const ThreadPool& tp) = delete;
    public:
        // 有可能有多个执行流进入该函数，但是只能创建一个对象
        static ThreadPool<T>* Getinstance(int threadsize = defaultThreadSize)
        {
            LOG(loglevel::DEBUG) << "获取线程池单例...";
            if(!_inc){
                mutexguard lock(_sm);   
                if(!_inc){
                    LOG(loglevel::INFO) << "线程池单例创建....";
                    _inc = new ThreadPool<T>(threadsize);
                }
            }
            return _inc;
        }

        ~ThreadPool(){}

        void Handler()
        {
            // 获取线程名字
            char name[128] = { 0 };
            pthread_getname_np(pthread_self(), name, sizeof(name));

            // 从任务队列中获取任务
            while(true){
                T t;
                {
                    // 加锁访问任务队列，任意时刻只能有一个线程访问任务队列
                    mutexguard lock(_mutex);

                    // 当线程池终止了，但有可能还有线程再等待，此时已经没有任务，其他的线程都已经被回收了，这些线程会导致内存泄露
                    // 但是如果直接叫醒所有线程，它们不会退出循环，而是继续等待
                    // 所以在进行等待的时候，要判断线程池是否还在运行，如果已经结束，并且任务队列为空，则不需要等待
                    // 一个不满足，就必须等待
                    while(_isRunning && _taskManager.empty()){
                        // 任务队列为空，线程进行等待
                        _sleepernumber++;
                        _cond.Wait(_mutex);
                        _sleepernumber--;
                    }
                    // 当线程池已经终止了&&任务队列为空，就让线程结束
                    if(!_isRunning && _taskManager.empty()){
                        LOG(loglevel::INFO) << name << "退出";
                        break;
                    }

                    // 获取任务
                    t = _taskManager.front();
                    _taskManager.pop();
                }
                // 执行任务
                // 当一个线程加锁拿出任务后，这个任务已经从任务队列中消失了，只属于该线程私有，所以先解锁，再执行，提高效率。
                t();
            }
        }

        // 让线程池终止
        void Stop()
        {
            if(!_isRunning) return;
            _isRunning = false;
            LOG(loglevel::INFO) << "线程池已经被终止";

            //  线程池结束就让所有等待的线程苏醒，否则它们不会退出
            WakeUpAll();
        }

        // 回收线程
        void Join()
        {
            if(_isRunning) return;
            for(auto& thread : _threads){
                thread.Join();
            }
        }

        // 向任务队列中新增任务
        bool emplace(const T& task)
        {
            // 任意时刻，都只允许只有一个线程插入任务
            mutexguard lock(_mutex);
            if(!_isRunning) return false;
            _taskManager.emplace(task);            
            if(_sleepernumber){
                WakeUpOne();
            }
            return true;
        }

    private:
        std::vector<Thread> _threads; // 线程池
        int _num;                     // 线程个数
        std::queue<T> _taskManager;   // 任务队列
        Mutex _mutex;                 // 互斥锁
        cond _cond;                   // 信号量

        bool _isRunning;              // 线程池是否运行
        int _sleepernumber;           // 当前等待的线程个数

        static ThreadPool<T>* _inc;   // 未来实例化出的对象
        static Mutex _sm;             // 用来实现单例模式
    };

    // 初始化静态成员
    template <typename T>
    ThreadPool<T>* ThreadPool<T>::_inc = nullptr;

    template <typename T>
    Mutex ThreadPool<T>::_sm;
}

#endif